﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Collections.ObjectModel;
using System.Xml.Linq;
using System.Collections.Specialized;
using System.ComponentModel;
using AMail.Common.Interfaces.Data;
using Elentok.Common;
using AMail.Modules.Data.Actions;
using Elentok.Common.Collections;
using Elentok.Common.Helpers;

namespace AMail.Modules.Data.Internals
{
  internal class MailFolder : IMailFolder
  {
    #region Private Data Members
    private string name;
    private IMailFolder parentFolder;
    private IMailFolder previousParentFolder;
    #endregion

    #region Ctors
    internal MailFolder()
    {
      Messages = new VeryObservableCollection<IMailMessage>();
      Subfolders = new VeryObservableCollection<IMailFolder>();

      Messages.CollectionChanged += new NotifyCollectionChangedEventHandler(Messages_CollectionChanged);
      Messages.ItemPropertyChanged += new EventHandler<ItemPropertyChangedEventArgs<IMailMessage>>(Messages_ItemPropertyChanged);
      Messages.Clearing += new EventHandler(Messages_Clearing);
      Messages.ItemPropertyChanging += new EventHandler<ItemPropertyChangingEventArgs<IMailMessage>>(Messages_ItemPropertyChanging);

      Subfolders.CollectionChanged += new NotifyCollectionChangedEventHandler(Subfolders_CollectionChanged);
      Subfolders.ItemPropertyChanged += new EventHandler<ItemPropertyChangedEventArgs<IMailFolder>>(Subfolders_ItemPropertyChanged);
      Subfolders.Clearing += new EventHandler(Subfolders_Clearing);
    }

    internal MailFolder(string name) : this()
    {
      this.Name = name;
    }

    internal MailFolder(XElement element)
      : this(element.GetStringAttribute("Name"))
    {
      foreach(XElement subfolder in element.Elements("Folder"))
      {
        IMailFolder folder = new MailFolder(subfolder);
        folder.ParentFolder = this;
      }

      foreach (XElement messageElement in element.Elements("Message"))
      {
        IMailMessage message = new MailMessage(messageElement);
        message.ParentFolder = this;
      }
    }
    #endregion

    #region Event Handlers

    private void Subfolders_Clearing(object sender, EventArgs e)
    {
      var foldersToDelete = new List<IMailFolder>(Subfolders);

      foreach (IMailFolder folder in Subfolders)
      {
        folder.ActionPerformed -= new EventHandler<MailActionEventArgs>(Item_ActionPerformed);
        folder.ParentFolder = null;
      }

      if (foldersToDelete.Count > 0)
        RaiseActionPerformed(new DeleteFoldersAction(foldersToDelete, this));
    }

    private void Subfolders_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs<IMailFolder> e)
    {
      OnPropertyChanged("Subfolders");
    }

    private void Subfolders_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      #region Disconnect Old Items
      if (e.OldItems != null)
      {
        var deletedFolders = new List<IMailFolder>();
        var movedFolders = new List<IMailFolder>();

        foreach (IMailFolder folder in e.OldItems)
        {
          folder.ActionPerformed -= new EventHandler<MailActionEventArgs>(Item_ActionPerformed);

          if (folder.ParentFolder == this)
            folder.ParentFolder = null;
          
          if (folder.ParentFolder == null)
            deletedFolders.Add(folder);
          else
            movedFolders.Add(folder);
        }

        #region Raise Action Performed
        if (deletedFolders.Count > 0)
          RaiseActionPerformed(new DeleteFoldersAction(deletedFolders, this));

        if (movedFolders.Count > 0)
          RaiseActionPerformed(new MoveFoldersAction(movedFolders, this));
        #endregion
      }
      #endregion

      #region Connect New Items
      if (e.NewItems != null)
      {
        var newFolders = new List<IMailFolder>();

        foreach (IMailFolder folder in e.NewItems)
        {
          folder.ActionPerformed += new EventHandler<MailActionEventArgs>(Item_ActionPerformed);

          if (folder.PreviousParentFolder == null)
            newFolders.Add(folder);

          if (folder.ParentFolder != this)
            folder.ParentFolder = this;
        }

        #region Raise Action Performed
        if (newFolders.Count > 0)
          RaiseActionPerformed(new AddFoldersAction(newFolders));

        #endregion
      }
      #endregion

      OnPropertyChanged("Subfolders");
    }

    private void Messages_Clearing(object sender, EventArgs e)
    {
      var messagesToDelete = new List<IMailMessage>(Messages);

      foreach (IMailMessage message in Messages)
      {
        message.ActionPerformed -= new EventHandler<MailActionEventArgs>(Item_ActionPerformed);
        message.ParentFolder = null;
      }

      if (messagesToDelete.Count > 0)
        RaiseActionPerformed(new DeleteMessagesAction(messagesToDelete, this));
    }

    private void Messages_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs<IMailMessage> e)
    {
      OnPropertyChanged("Messages");
    }

    private void Messages_ItemPropertyChanging(object sender, ItemPropertyChangingEventArgs<IMailMessage> e)
    {
      if (e.PropertyName == "Subject")
      {
        RaiseActionPerformed(new ModifyMessagePropertyAction(e.Item, e.PropertyName, e.Item.Subject));
      }
      else if (e.PropertyName == "Encoding")
      {
        RaiseActionPerformed(new ModifyMessagePropertyAction(e.Item, e.PropertyName, e.Item.Encoding));
      }
    }

    private void Messages_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      #region Disconnect Old Items
      if (e.OldItems != null)
      {
        var deletedMessages = new List<IMailMessage>();
        var movedMessages = new List<IMailMessage>();

        foreach (IMailMessage message in e.OldItems)
        {
          message.ActionPerformed -= new EventHandler<MailActionEventArgs>(Item_ActionPerformed);

          if (message.ParentFolder == this)
            message.ParentFolder = null;

          if (message.ParentFolder == null)
            deletedMessages.Add(message);
          else
            movedMessages.Add(message);
        }

        #region Raise Action Performed
        if (deletedMessages.Count > 0)
          RaiseActionPerformed(new DeleteMessagesAction(deletedMessages, this));

        if (movedMessages.Count > 0)
          RaiseActionPerformed(new MoveMessagesAction(movedMessages, this));
        #endregion
      }
      #endregion

      #region Connect New Items
      if (e.NewItems != null)
      {
        var newMessages = new List<IMailMessage>();

        foreach (IMailMessage message in e.NewItems)
        {
          message.ActionPerformed += new EventHandler<MailActionEventArgs>(Item_ActionPerformed);

          if (message.PreviousParentFolder == null)
            newMessages.Add(message);

          if (message.ParentFolder != this)
            message.ParentFolder = this;
        }

        if (newMessages.Count > 0)
          RaiseActionPerformed(new AddMessagesAction(newMessages));
      }
      #endregion

      OnPropertyChanged("Messages");
    }

    private void Item_ActionPerformed(object sender, MailActionEventArgs e)
    {
      RaiseActionPerformed(e.Action);
    }

    #endregion

    public override string ToString()
    {
      return string.Format("MailFolder: '{0}'", Name);
    }

    #region IMailFolder Members

    public VeryObservableCollection<IMailMessage> Messages { get; private set; }
    public VeryObservableCollection<IMailFolder> Subfolders { get; private set; }

    public virtual IMailFolder ParentFolder
    {
      get
      {
        return parentFolder;
      }
      set
      {
        if (this.parentFolder == value)
          return;

        this.PreviousParentFolder = this.parentFolder;
        this.parentFolder = value;

        #region Remove from old parent
        if (this.PreviousParentFolder != null && this.PreviousParentFolder.Subfolders.Contains(this))
          this.PreviousParentFolder.Subfolders.Remove(this);
        #endregion


        #region Add to new parent
        if (parentFolder != null && !parentFolder.Subfolders.Contains(this))
          parentFolder.Subfolders.Add(this);
        #endregion

        OnPropertyChanged("ParentFolder");
      }
    }


    /// <summary>
    /// Gets or sets the PreviousParentFolder
    /// </summary>
    public IMailFolder PreviousParentFolder
    {
      get
      {
        return this.previousParentFolder;
      }
      private set
      {
        if (this.previousParentFolder == value)
          return;

        this.previousParentFolder = value;
        OnPropertyChanged("PreviousParentFolder");
      }
    }

    public virtual IMailArchive OwnerArchive
    {
      get
      {
        if (parentFolder == null)
          return null;

        return parentFolder.OwnerArchive;
      }
    }

    /// <summary>
    /// Gets or sets the Name
    /// </summary>
    public string Name
    {
      get
      {
        return name;
      }
      set
      {
        if (name == value)
          return;

        name = value;
        OnPropertyChanged("Name");
      }
    }

    public void WriteXml(XmlTextWriter writer)
    {
      writer.WriteStartElement("Folder");
      writer.WriteAttributeString("Name", Name);
      foreach (IMailFolder folder in Subfolders)
        folder.WriteXml(writer);
      foreach (IMailMessage message in Messages)
        message.WriteXml(writer);
      writer.WriteEndElement();
    }

    public virtual IMailFolder CreateSubfolder(string name)
    {
      var folder = new MailFolder(name);
      folder.ParentFolder = this;
      return folder;
    }

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises the PropertyChanged event
    /// </summary>
    protected void OnPropertyChanged(string propertyName)
    {
      PropertyChangedEventHandler handler = PropertyChanged;
      if (handler != null)
        handler(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

    #region IActionableObject Members

    public event EventHandler<MailActionEventArgs> ActionPerformed;

    protected void RaiseActionPerformed(IMailAction action)
    {
      var handler = ActionPerformed;
      if (handler != null)
        handler(this, new MailActionEventArgs(action));
    }

    #endregion
  }

  internal class RootMailFolder : MailFolder
  {
    IMailArchive _owner;
    public RootMailFolder(IMailArchive owner) : base("Root")
    {
      _owner = owner;
    }

    public RootMailFolder(IMailArchive owner, XElement element) : base(element)
    {
      _owner = owner;
    }

    public override IMailArchive OwnerArchive
    {
      get
      {
        return _owner;
      }
    }

    public override IMailFolder ParentFolder
    {
      get
      {
        return base.ParentFolder;
      }
      set
      {
      }
    }
  }
}
